/*
Copyright (C) 2010  Christoph Schnack, Berlin (Germany)

This file is part of Tribuit.

    Tribuit is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Tribuit is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Tribuit.  If not, see <http://www.gnu.org/licenses/>.
*/
package Statistics;

import java.io.*;
import java.util.*;

import OtherDialogs.Loader;
import Settings.Settings;
import Storage.*;
import GuiTools.*;
/**
 * Stores statistics for every word concerning date and count of correct/false answers.
 * 
 * @author Christoph Schnack
 *
 */

public class Statistics {
	Vector<VocabularyStatistic> savedVocStats = new Vector<VocabularyStatistic>();
	Vector<VocabularyStatistic> toAddVocStats = new Vector<VocabularyStatistic>();
	Vector<VocabularyStatistic> toDeleteVocStats = new Vector<VocabularyStatistic>();
	Storage MAINVOC;
	Statistics STATISTICS;
	Settings SET;
	boolean SAVED = false,verbose = false;
	
	public Statistics(Storage mv,Settings set,Loader ldr){
		MAINVOC=mv;
		SET=set;
		STATISTICS=this;
		verbose = SET.isVerbose();
		loadStatistics(ldr);
	}
	
	public void addVocabulary(String name,boolean newVoc){
		VocabularyStatistic newVocStat = new VocabularyStatistic(name);
		if(newVoc){ if(!toAddVocStats.contains(newVocStat))toAddVocStats.add(newVocStat);}
		else if(!savedVocStats.contains(newVocStat))savedVocStats.add(newVocStat);
		SAVED = false;
	}
	
	public void removeVocabularyStatistic(String voc){
		VocabularyStatistic toRemove;
		for(int i=0;i<savedVocStats.size();i++){
			toRemove = savedVocStats.elementAt(i);
			if(voc.equals(toRemove.getName(true))){
				toDeleteVocStats.add(toRemove);
				savedVocStats.remove(toRemove);
				SAVED = false;
				return;
			}
		}
		for(int i=0;i<toAddVocStats.size();i++){
			toRemove = toAddVocStats.elementAt(i);
			if(voc.equals(toRemove.getName(true))){
				toAddVocStats.remove(toRemove);
				SAVED = false;
				return;
			}
		}
	}
	
	public void addWordToVocabulary(String vocName,String wordName, WordStatistic stat,boolean saved){
		if(!saved)getVocabularyStatistic(vocName,true).addNewWord(wordName, stat);
		else getVocabularyStatistic(vocName,false).addSavedWord(wordName, stat);
	}
	
	public void deleteWordFromVocabulary(String wordName,String vocName){
		if(hasVocabularyStatistic(vocName,true)){
			getVocabularyStatistic(vocName,true).deleteWord(wordName);
		}
	}
	
	public void replaceWordInVocabulary(String oldName,String newName,String vocName,boolean copyStats){
		if(hasVocabularyStatistic(vocName,true)){
			WordStatistic ws = getVocabularyStatistics(vocName,true).get(oldName);
			addWordToVocabulary(vocName,newName, (copyStats)?new WordStatistic(ws.getCorrect(),ws.getWrong(),ws.getLastCheckedDate()):new WordStatistic(),false);
			getVocabularyStatistic(vocName,true).deleteWord(oldName);
		}
	}
	
	public void resetStatistic(String voc, String name){
		getVocabularyStatistic(voc,true).resetWord(name);
	}
	
	public VocabularyStatistic getVocabularyStatistic(String name,boolean all){
		if(savedVocStats.size()>0){
			for(int i=0;i<savedVocStats.size();i++){
				if(savedVocStats.elementAt(i).getName(true).equals(name))return savedVocStats.elementAt(i);
			}
		}
		if(all){
			if(toAddVocStats.size()>0){
				for(int i=0;i<toAddVocStats.size();i++){
					if(toAddVocStats.elementAt(i).getName(true).equals(name))return toAddVocStats.elementAt(i);
				}
			}
		}
		else{
			if(toDeleteVocStats.size()>0){
				for(int i=0;i<toDeleteVocStats.size();i++){
					if(toDeleteVocStats.elementAt(i).getName(true).equals(name))return toDeleteVocStats.elementAt(i);
				}
			}
		}
		return null;
	}
	
	public HashMap<String,WordStatistic> getVocabularyStatistics(String vocName,boolean all){
		return (hasVocabularyStatistic(vocName,all))? getVocabularyStatistic(vocName,all).getStatistics(all):null;
	}
	
	public void renameVocabulary(String oldName,String newName){
		if(hasVocabularyStatistic(oldName,true)){
			getVocabularyStatistic(oldName,true).setName(newName);
		}
	}
	
	public boolean hasVocabularyStatistic(String name,boolean all){
		if(savedVocStats.size()>0){
			for(int i=0;i<savedVocStats.size();i++){
				if(savedVocStats.elementAt(i).getName(true).equals(name))return true;
			}
		}
		if(all){
			if(toAddVocStats.size()>0){
				for(int i=0;i<toAddVocStats.size();i++){
					if(toAddVocStats.elementAt(i).getName(true).equals(name))return true;
				}
			}
		}
		else{
			if(toDeleteVocStats.size()>0){
				for(int i=0;i<toDeleteVocStats.size();i++){
					if(toDeleteVocStats.elementAt(i).getName(true).equals(name))return true;
				}
			}
		}
		return false;
	}
	
	public class VocabularyStatistic{
		String savedName,newName;
		HashMap<String,WordStatistic> savedHM,toAddHM,toDeleteHM;
		public VocabularyStatistic(String nm){
			savedName = nm;
			newName = savedName;
			savedHM = new HashMap<String,WordStatistic>();
			toAddHM = new HashMap<String,WordStatistic>();
			toDeleteHM = new HashMap<String,WordStatistic>();
		}
		
		public String getName(boolean currentName){
			return (currentName)?newName:savedName;
		}
		
		public void setName(String n){
			newName=n;
			SAVED = false;
		}
		
		public void addSavedWord(String s, WordStatistic ws){
			savedHM.put(s,ws);
		}
		
		public void addNewWord(String s, WordStatistic ws){
			toAddHM.put(s,ws);
			SAVED = false;
		}
		
		public void deleteWord(String s){
			if(savedHM.containsKey(s)){
				toDeleteHM.put(s,savedHM.get(s));
				savedHM.remove(s);
			}
			else{
				if(toAddHM.containsKey(s)) toAddHM.remove(s);
			}
			SAVED = false;
		}
		
		public void resetWord(String word){
			if(savedHM.containsKey(word)) savedHM.get(word).reset();
			else if(toAddHM.containsKey(word)) toAddHM.get(word).reset();
			SAVED = false;
		}
		
		public HashMap<String,WordStatistic> getStatistics(boolean all){
			HashMap<String,WordStatistic> result = new HashMap<String,WordStatistic>();
			for(String s : savedHM.keySet().toArray(new String[]{})){
				result.put(s, savedHM.get(s));
			}
			if(!all){
				if(!toDeleteHM.isEmpty()){
					for(String s : toDeleteHM.keySet().toArray(new String[]{})){
						result.put(s, toDeleteHM.get(s));
					}
				}
			}
			else{
				if(!toAddHM.isEmpty()){
					for(String s : toAddHM.keySet().toArray(new String[]{})){
						result.put(s, toAddHM.get(s));
					}
				}
			}
			return result;
		}
		
		
		public void setSaved(){
			for(String s : toAddHM.keySet().toArray(new String[]{})){
				savedHM.put(s, toAddHM.get(s));
			}
			toAddHM = new HashMap<String,WordStatistic>();
			toDeleteHM = new HashMap<String,WordStatistic>();
		}
		
	}
	
	public class WordStatistic{
		int correct=0,wrong=0;
		long lastChecked=0;
		
		public WordStatistic(int c, int w, long date){
			correct=c;
			wrong=w;
			lastChecked = date;
		}
		
		public WordStatistic(){}
		
		public void reset(){
			correct=0;
			wrong=0;
			lastChecked=0;
		}
		
		public void increaseCorrects(){
			correct++;
			lastChecked = GregorianCalendar.getInstance().getTimeInMillis();
			
		}
		public void increaseWrongs(){
			wrong++;
			lastChecked = GregorianCalendar.getInstance().getTimeInMillis();
		}
		
		public int getCorrect(){return correct;}
		public int getWrong(){return wrong;}
		public long getLastCheckedDate(){return lastChecked;}
		public String getData(){return correct+":"+wrong+":"+lastChecked;}
		public int getPercentWrong(){
			int sum = wrong+correct;
			if(wrong==0)return 0;
			float unit = sum*0.01F;
			return Math.round(wrong/unit);
		}
	}
	
	/**
	 * Reads out or creates settings/statistics.trib and fills Statistitcs with its data.
	 * The record files format is: VOC1=WORD1A-WORD1B:CORRECT:WRONG:DATE$VOC2=...
	 * $ separates Vocabularies, = vocabulary name from word data and : the word data. 
	 */
	public void loadStatistics(Loader loader){
		try{
			if(verbose) System.out.println("\n#---INITIALIZING STATISTICS---");
			File f = new File(GuiTools.TRIBUITHOME+File.separator+"settings"+File.separator+"statistics.trib");
			if(f.exists()){
				if(verbose) System.out.println("# statistic file found");
				//read data
				BufferedReader bf = new BufferedReader(new InputStreamReader(new FileInputStream(f),"UTF-8"));
				StringBuffer sb = new StringBuffer();
				String line;
				while((line=bf.readLine())!=null){
					sb.append(line);
				}
				bf.close();
				//fill STATISTICS data
				if(sb.length()>0 && MAINVOC.hasVocs()){
					String readouts = sb.toString();
					String[] vocs = readouts.split("%");
					if(verbose) System.out.println("# adding statistics:");
					int loaderCounter = 0;
					Vocabulary voc;
					String[] wordsplit,vocabulary,words;
					Vector<String> wordContainer = new Vector<String>();
					for(String split1 : vocs){
						vocabulary = split1.split("=");
						words = vocabulary[1].split(">");
						if(MAINVOC.hasVocabulary(vocabulary[0])){
							addVocabulary(vocabulary[0],false);
							if(verbose) System.out.println("#   " + vocabulary[0] + "(" + words.length+")");
							if(!words[0].equals("")){
								voc = MAINVOC.getVocabulary(vocabulary[0]);
								wordContainer.removeAllElements();
								for(Word w : voc.getWords()) wordContainer.add(w.getName());
								loaderCounter = 0;
								loader.setVocabulary("loading statistics: "+voc.getName(),words.length);
								for(String word:words){
									loader.setProgress(++loaderCounter);
									wordsplit = word.split(":");
									if(wordContainer.contains(wordsplit[0])) addWordToVocabulary(vocabulary[0], wordsplit[0], new WordStatistic(Integer.parseInt(wordsplit[1]),Integer.parseInt(wordsplit[2]),Long.parseLong(wordsplit[3])),true);
								}
							}
						}
					}
				}
			}
			else{
				if(verbose) System.out.print("# creating statistics file...");
				if(f.createNewFile()){if(verbose) System.out.println("successfully");}
				else if(verbose) System.out.print(" FAILED!");
			}
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}
	/**
	 * 
	 * @return
	 */
	public boolean updateStatistics(Loader loader){
		if(MAINVOC.hasVocs()){
			Vocabulary[] vocs = MAINVOC.getVocabularies();
			int loaderCounter = 0;
			for(Vocabulary v : vocs){
				if(!hasVocabularyStatistic(v.getName(),false)){
					addVocabulary(v.getName(),false);
				}
				if(v.hasWords()){
					String name = v.getName();
					//create new statistics for new words
					HashMap<String,WordStatistic> stats = getVocabularyStatistics(name,true);
					Vector<String> existingWords = new Vector<String>();
					loaderCounter = 0;
					loader.setVocabulary("updating statistics: "+v.getName(),v.vcb.length);
					for(Word w : v.getWords()){
						loader.setProgress(++loaderCounter);
						existingWords.add(w.getName());
						if(!stats.containsKey(w.getName()))addWordToVocabulary(name, w.getName(), new WordStatistic(),true);
					}
					//remove existing statistics for not existing words
					HashMap<String,WordStatistic> updatedStats = STATISTICS.getVocabularyStatistics(name,true);
					String[] keys = updatedStats.keySet().toArray(new String[]{});
					for(String s : keys){
						if(!existingWords.contains(s))deleteWordFromVocabulary(name, s);
					}
				}
			}
		}
		loader.setRunning(false);
		return true;
	}
	
	public void setVocsSaved(){
		SAVED = true;
		for(int i=0;i<toAddVocStats.size();i++){
			savedVocStats.add(toAddVocStats.elementAt(i));
		}
		toAddVocStats = new Vector<VocabularyStatistic>();
		toDeleteVocStats = new Vector<VocabularyStatistic>();
	}
	
	public boolean isSaved(){
		return SAVED;
	}
	/**
	 * 
	 *@author Christoph Schnack
	 * @param all including possible new words or not
	 * @return
	 */
	public boolean saveStatistics(boolean all){
		if(savedVocStats.size()>0 || toDeleteVocStats.size()>0 || (all&&toAddVocStats.size()>0)){
		    try{
		    	if(verbose)System.out.println("#saving statistics");
		    	BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(GuiTools.STATISTICFILEPATH),"UTF-8"));
		    	HashMap<String,WordStatistic> vocabulary;
		    	String[] wordKeys;
		    	Vector<VocabularyStatistic> toSave = new Vector<VocabularyStatistic>();
		    	for(int i=0;i<savedVocStats.size();i++){
	    			toSave.add(savedVocStats.elementAt(i));
	    		}
		    	if(!all){
		    		for(int i=0;i<toDeleteVocStats.size();i++){
		    			toSave.add(toDeleteVocStats.elementAt(i));
		    		}
		    	}
		    	else{
		    		for(int i=0;i<toAddVocStats.size();i++){
		    			toSave.add(toAddVocStats.elementAt(i));
		    		}
		    	}
		    	for(int i=0;i<toSave.size();i++){
		    		vocabulary = getVocabularyStatistics(toSave.elementAt(i).getName(all),all);
	    			if(vocabulary!=null && !vocabulary.isEmpty()){
			    		writer.write(toSave.elementAt(i).getName(all)+"=");
	    				wordKeys = vocabulary.keySet().toArray(new String[]{});
	    				for(String word : wordKeys){
	    					writer.write(word+":"+vocabulary.get(word).getData()+">");
	    				}
	    				writer.write("%");
	    				if(all) toSave.elementAt(i).setSaved();
	    			}
		    	}
		    	if(all)setVocsSaved();
		    	writer.close();
		    }
		    catch(IOException e){e.printStackTrace();}
		}
		return true;
	}//save
		
	public void increaseCorrects(String vocName, String wordName){
		getVocabularyStatistics(vocName,true).get(wordName).increaseCorrects();
	}
	
	public void increaseWrongs(String vocName, String wordName){
		getVocabularyStatistics(vocName,true).get(wordName).increaseWrongs();
	}
}
